﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using QueryBuilder.Core.Query.Exceptions;
using QueryBuilder.Core.Query.Properties;
using QueryBuilder.Core.Query.Type;

namespace QueryBuilder.Core.Query.Presenters.Mysql
{
    /// <summary>
    /// 
    /// </summary>
    public class MysqlConstraintPresenter : AbstractPresenter
    {
        private const string AS = "AS";
        private const string CAST = "CAST";
        private const string Collationtype = "CollationType";
        private const string Comparision = "/";
        private const string CONCAT = "CONCAT";
        private const string DataTypeName = "DataType";
        private string CHAR;
        private string UNSIGEND;

        /// <summary>
        /// Initializes a new instance of the <see cref="MysqlConstraintPresenter"/> class.
        /// </summary>
        /// <param name="typeMappingsRegistry">The type mappings registry.</param>
        public MysqlConstraintPresenter(TypeMappingsRegistry typeMappingsRegistry)
            : base(typeMappingsRegistry)
        {
            this.ComparisonMappings.Add(CONCAT, this.PresentConcat);
            this.ComparisonMappings.Add(Comparisons.AND, this.PresentAnd);
            this.ComparisonMappings.Add(Comparision, this.PresentDividing);
            this.ComparisonMappings.Add(CAST, this.PresentCast);

            this.TypeMappings.Add(Collationtype, this.PresentCollationType);
            this.TypeMappings.Add(DataTypeName, this.PresentDataType);
            this.CHAR = "CHAR";
            this.UNSIGEND = "UNSIGNED";
        }

        /// <summary>
        /// Gets the close quote.
        /// </summary>
        /// <value>The close quote.</value>
        public override string CloseQuote
        {
            get { return "'"; }
        }

        /// <summary>
        /// Gets the open quote.
        /// </summary>
        /// <value>The open quote.</value>
        public override string OpenQuote
        {
            get { return "'"; }
        }

        /// <summary>
        /// Presents the cast.
        /// </summary>
        /// <param name="constraints">The constraints.</param>
        private void PresentCast(IEnumerable<Constraint> constraints)
        {
            this.Result.Append(CAST);
            this.Result.Append(SqlFragments.OpenBracket);
            constraints.ElementAt(0).Accept(this);
            this.Result.Append(SqlFragments.Space);
            this.Result.Append(AS);
            this.Result.Append(SqlFragments.Space);
            constraints.ElementAt(1).Accept(this);
            this.Result.Append(SqlFragments.CloseBracket);
        }

        /// <summary>
        /// Presents the type of the data.
        /// </summary>
        /// <param name="value">The value.</param>
        protected virtual void PresentDataType(object value)
        {
            if ((DataType) value == DataType.CHAR)
            {
                this.Result.Append(this.CHAR);
            }
            if ((DataType) value == DataType.UNSIGNED)
            {
                this.Result.Append(this.UNSIGEND);
            }
        }

        /// <summary>
        /// Presents the type of the collation.
        /// </summary>
        /// <param name="obj">The obj.</param>
        protected virtual void PresentCollationType(object obj)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Presents the dividing.
        /// </summary>
        /// <param name="constraints">The constraints.</param>
        protected virtual void PresentDividing(IEnumerable<Constraint> constraints)
        {
            if (constraints.Count() != 2)
            {
                throw new IllegalOperationException(string.Format(CultureInfo.CurrentCulture, Resources.IllegalCountArguments,
                                                                  SqlFragments.DIVIDING));
            }

            constraints.First().Accept(this);

            this.Result.Append(Comparision);

            constraints.Last().Accept(this);
        }

        /// <summary>
        /// Presents the and.
        /// </summary>
        /// <param name="constraints">The constraints.</param>
        protected virtual void PresentAnd(IEnumerable<Constraint> constraints)
        {
            if (constraints.Count() != 2)
            {
                throw new IllegalOperationException(string.Format(CultureInfo.CurrentCulture, Resources.IllegalCountArguments,
                                                                  SqlFragments.AND));
            }

            constraints.First().Accept(this);

            this.Result.Append(Comparisons.AND);

            constraints.Last().Accept(this);
        }

        /// <summary>
        /// Presents the concat.
        /// </summary>
        /// <param name="constraints">The constraints.</param>
        protected virtual void PresentConcat(IEnumerable<Constraint> constraints)
        {
            this.Result.Append(CONCAT);
            this.Result.Append(SqlFragments.OpenBracket);

            foreach (Constraint constraint in constraints)
            {
                constraint.Accept(this);
                if (constraint != constraints.Last())
                {
                    this.Result.Append(SqlFragments.Comma);
                }
            }

            this.Result.Append(SqlFragments.CloseBracket);
        }
    }
}